from django.db import models
from django.core.validators import MinValueValidator
from django.utils.translation import get_language
from django.utils.encoding import smart_text


class MaterialCategory(models.Model):
    """材料类别，分层级树状表示。
    同一父节点下的子节点名字不能相同
    """
    id = models.AutoField(primary_key=True)
    parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.CASCADE)
    level = models.IntegerField(default=0, validators=(MinValueValidator(0),))
    name_zh = models.CharField(max_length=255)
    name_en = models.CharField(max_length=255)
    leaf = models.BooleanField(default=True)

    class Meta:
        unique_together = (('parent', 'name_zh'),
                           ('parent', 'name_en'))
        ordering = ('id',)

    def __str__(self):
        return self.name_en

    @property
    def name(self):
        lang = get_language()
        return getattr(self, 'name_' + lang[:2])

    def delete(self, using=None, keep_parents=False):
        if self.parent.children.count() == 1:
            self.parent.leaf = True
            self.parent.save(update_fields=('leaf',))
        super(MaterialCategory, self).delete(using, keep_parents)

    def save(self, *args, **kwargs):
        if self.parent:
            self.level = self.parent.level + 1
            if self.parent.leaf:
                self.parent.leaf = False
                self.parent.save()
        super(MaterialCategory, self).save(*args, **kwargs)

    def to_dict(self):
        return {'id': self.id,
                'pid': self.parent_id,
                'level': self.level,
                'name_zh': self.name_zh,
                'name_en': self.name_en,
                'leaf': self.leaf}


class MaterialTag(models.Model):
    """材料标签，记录标签名称和引用计数。引用计数由系统进行统计，无需显式添加或删除。

    `name`:  标签名称
    `count`: 引用计数，初始值为1，即包含这个标签的数据的数目
    """
    name = models.CharField(max_length=32, unique=True)
    count = models.IntegerField(default=1, validators=(MinValueValidator(0),))

    class Meta:
        ordering = ('-count',)

    def __str__(self):
        return self.name

    def to_dict(self):
        return {'id': self.id,
                'name': self.name,
                'count': self.count}

    @staticmethod
    def add(name: str):
        """
        如果已经存在则将引用计数+1，否则直接添加一个新标签
        :param name: 标签名称
        :type name: str
        :return: 返回添加或修改后的标签
        :rtype: MaterialTag
        """
        name = smart_text(name).strip()
        mt = None
        try:
            mt = MaterialTag.objects.get(name=name)
            MaterialTag.inc_count(mt)
        except MaterialTag.DoesNotExist:
            if name:
                mt = MaterialTag.objects.create(name=name)
                mt.save()
        finally:
            return mt

    @staticmethod
    def inc_count(tag):
        """
        将`tag`的引用计数 + 1（更新到数据库）
        :param tag: 待更新的标签实例或标签名称
        :type tag: MaterialTag, str
        :return: None
        """
        if isinstance(tag, str):
            tag = tag.strip()
            try:
                tag = MaterialTag.objects.get(name=tag)
            except MaterialTag.DoesNotExist:
                MaterialTag.objects.create(name=tag)
                return
        if not isinstance(tag, MaterialTag):
            raise ValueError('`tag` must be an instance of `MaterialTag` or tag name.')
        tag.count += 1
        tag.save()

    @staticmethod
    def dec_count(tag):
        """
        将`tag`的引用计数 - 1（更新到数据库）
        :param tag: 待更新的标签实例或标签名称
        :type tag: MaterialTag, str
        :return: None
        """
        if isinstance(tag, str):
            tag = tag.strip()
            tag = MaterialTag.objects.get_or_create(name=tag)  # 这里不存在直接创建也没事，正好减为 0
        if not isinstance(tag, MaterialTag):
            raise ValueError('`tag` must be an instance of `MaterialTag` or tag name.')
        count = max(0, tag.count - 1)
        tag.count = count
        tag.save()
